
function button(id,parentNode,functionToCall,buttonType,buttonText,buttonSymbolId,x,y,width,height,textStyles,buttonStyles,shadeLightStyles,shadeDarkStyles,shadowOffset) {	
	var nrArguments = 15;
	if (arguments.length > 0) {
		if (arguments.length == nrArguments) {
			this.init(id,parentNode,functionToCall,buttonType,buttonText,buttonSymbolId,x,y,width,height,textStyles,buttonStyles,shadeLightStyles,shadeDarkStyles,shadowOffset);
		}
		else {
			alert("Error in Button ("+id+"): wrong nr of arguments! You have to pass over "+nrArguments+" parameters.");		
		}
	}
}

button.prototype.init = function(id,parentNode,functionToCall,buttonType,buttonText,buttonSymbolId,x,y,width,height,textStyles,buttonStyles,shadeLightStyles,shadeDarkStyles,shadowOffset) {
	this.id = id; //the id where all new content is appended to
	this.parentNode = parentNode; //the id or node reference of the parent group where the button can be appended
	this.functionToCall = functionToCall; //function to be called if button was pressed
	this.buttonType = buttonType; //button type: currently either "rect" or "ellipse"
	this.buttonText = buttonText; //default value to be filled in when button is created
	this.buttonSymbolId = buttonSymbolId; //id to a symbol to be used as a button graphics
	this.x = x; //left of button rectangle
	this.y = y; //top of button rectangle
	this.width = width; //button rectangle width
	this.height = height; //button rectangle height
	this.textStyles = textStyles; //array of literals containing text styles
	if (!this.textStyles["font-size"]) {
		this.textStyles["font-size"] = 12;
	}	
	this.buttonStyles = buttonStyles; //the fill color of the button rectangle or ellipse
	this.shadeLightStyles = shadeLightStyles; //light fill color simulating 3d effect
	this.shadeDarkStyles = shadeDarkStyles; //dark fill color simulating 3d effect
	this.shadowOffset = shadowOffset; //shadow offset in viewBox units
	this.upperLeftLine = null; //later a reference to the upper left line simulating 3d effect
	this.buttonRect = null; //later a reference to the button area (rect)
	this.buttonTextElement = null; //later a reference to the button text
	this.buttonSymbolInstance = null; //later a reference to the button symbol
	this.deActivateRect = null; //later a reference to a rectangle that can be used to deactivate the button
	this.activated = true; //a property indicating if button is activated or not
	this.lowerRightLine = null; //later a reference to the lower right line simulating 3d effect
	this.createButton(); //method to initialize button
	this.timer = new Timer(this); //a Timer instance for calling the functionToCall
	this.timerMs = 200; //a constant of this object that is used in conjunction with the timer - functionToCall is called after 200 ms
}

//create button
button.prototype.createButton = function() {
	var result = this.testParent();
		if (result) {
			//create upper left button line or ellipse
			if (this.buttonType == "rect") {
				this.upperLeftShadow = document.createElementNS(svgNS,"rect");
				this.upperLeftShadow.setAttributeNS(null,"x",this.x - this.shadowOffset);
				this.upperLeftShadow.setAttributeNS(null,"y",this.y - this.shadowOffset);
				this.upperLeftShadow.setAttributeNS(null,"width",this.width);
				this.upperLeftShadow.setAttributeNS(null,"height",this.height);
				this.upperLeftShadow.setAttributeNS(null,"points",this.x+","+(this.y+this.height)+" "+this.x+","+this.y+" "+(this.x+this.width)+","+this.y);
			}
			else if (this.buttonType == "ellipse") {
				this.upperLeftShadow = document.createElementNS(svgNS,"ellipse");
				this.upperLeftShadow.setAttributeNS(null,"cx",this.x + this.width * 0.5 - this.shadowOffset);
				this.upperLeftShadow.setAttributeNS(null,"cy",this.y + this.height * 0.5 - this.shadowOffset);
				this.upperLeftShadow.setAttributeNS(null,"rx",this.width * 0.5);
				this.upperLeftShadow.setAttributeNS(null,"ry",this.height * 0.5);
			}
			else {
				alert("buttonType '"+this.buttonType+"' not supported. You need to specify 'rect' or 'ellipse'");
			}
			for (var attrib in this.shadeLightStyles) {
				this.upperLeftShadow.setAttributeNS(null,attrib,this.shadeLightStyles[attrib]);
			}
			this.parentGroup.appendChild(this.upperLeftShadow);
    		
			//create lower right button line or ellipse
			if (this.buttonType == "rect") {
				this.lowerRightShadow = document.createElementNS(svgNS,"rect");
				this.lowerRightShadow.setAttributeNS(null,"x",this.x + this.shadowOffset);
				this.lowerRightShadow.setAttributeNS(null,"y",this.y + this.shadowOffset);
				this.lowerRightShadow.setAttributeNS(null,"width",this.width);
				this.lowerRightShadow.setAttributeNS(null,"height",this.height);
				this.lowerRightShadow.setAttributeNS(null,"points",this.x+","+(this.y+this.height)+" "+this.x+","+this.y+" "+(this.x+this.width)+","+this.y);
			}
			else if (this.buttonType == "ellipse") {
				this.lowerRightShadow = document.createElementNS(svgNS,"ellipse");
				this.lowerRightShadow.setAttributeNS(null,"cx",this.x + this.width * 0.5 + this.shadowOffset);
				this.lowerRightShadow.setAttributeNS(null,"cy",this.y + this.height * 0.5 + this.shadowOffset);
				this.lowerRightShadow.setAttributeNS(null,"rx",this.width * 0.5);
				this.lowerRightShadow.setAttributeNS(null,"ry",this.height * 0.5);
			}
			for (var attrib in this.shadeDarkStyles) {
				this.lowerRightShadow.setAttributeNS(null,attrib,this.shadeDarkStyles[attrib]);
			}
			this.parentGroup.appendChild(this.lowerRightShadow);
    		
			//create buttonRect
			if (this.buttonType == "rect") {
				this.buttonRect = document.createElementNS(svgNS,"rect");
				this.buttonRect.setAttributeNS(null,"x",this.x);
				this.buttonRect.setAttributeNS(null,"y",this.y);
				this.buttonRect.setAttributeNS(null,"width",this.width);
				this.buttonRect.setAttributeNS(null,"height",this.height);
			}
			else if (this.buttonType == "ellipse") {
				this.buttonRect = document.createElementNS(svgNS,"ellipse");
				this.buttonRect.setAttributeNS(null,"cx",this.x + this.width * 0.5);
				this.buttonRect.setAttributeNS(null,"cy",this.y + this.height * 0.5);
				this.buttonRect.setAttributeNS(null,"rx",this.width * 0.5);
				this.buttonRect.setAttributeNS(null,"ry",this.height * 0.5);
			}
			for (var attrib in this.buttonStyles) {
				this.buttonRect.setAttributeNS(null,attrib,this.buttonStyles[attrib]);
			}	
			this.buttonRect.setAttributeNS(null,"cursor","pointer");
			this.buttonRect.addEventListener("mousedown",this,false);
			this.buttonRect.addEventListener("mouseup",this,false);
			this.buttonRect.addEventListener("click",this,false);
			this.parentGroup.appendChild(this.buttonRect);
    		
			//call a helper method to create the text elements
			this.createButtonTexts();
			    		
			if (this.buttonSymbolId != undefined) {
				this.buttonSymbolInstance = document.createElementNS(svgNS,"use");
				this.buttonSymbolInstance.setAttributeNS(null,"x",(this.x + this.width / 2));
				this.buttonSymbolInstance.setAttributeNS(null,"y",(this.y + this.height / 2));
				this.buttonSymbolInstance.setAttributeNS(xlinkNS,"href","#"+this.buttonSymbolId);
				this.buttonSymbolInstance.setAttributeNS(null,"pointer-events","none");
				this.parentGroup.appendChild(this.buttonSymbolInstance);
			}
    		
			//create rectangle to deactivate the button
			if (this.buttonType == "rect") {
				this.deActivateRect = document.createElementNS(svgNS,"rect");
				this.deActivateRect.setAttributeNS(null,"x",this.x - this.shadowOffset);
				this.deActivateRect.setAttributeNS(null,"y",this.y - this.shadowOffset);
				this.deActivateRect.setAttributeNS(null,"width",this.width + this.shadowOffset * 2);
				this.deActivateRect.setAttributeNS(null,"height",this.height + this.shadowOffset * 2);
			}
			else if (this.buttonType == "ellipse") {
				this.deActivateRect = document.createElementNS(svgNS,"ellipse");
				this.deActivateRect.setAttributeNS(null,"cx",this.x + this.width * 0.5);
				this.deActivateRect.setAttributeNS(null,"cy",this.y + this.height * 0.5);
				this.deActivateRect.setAttributeNS(null,"rx",this.width * 0.5 + this.shadowOffset);
				this.deActivateRect.setAttributeNS(null,"ry",this.height * 0.5 + this.shadowOffset);
			}
    	
			this.deActivateRect.setAttributeNS(null,"fill","white");
			this.deActivateRect.setAttributeNS(null,"fill-opacity","0.5");
			this.deActivateRect.setAttributeNS(null,"stroke","none");
			this.deActivateRect.setAttributeNS(null,"display","none");
			this.deActivateRect.setAttributeNS(null,"cursor","default");
			if (this.buttonStyles["rx"]) {
				this.deActivateRect.setAttributeNS(null,"rx",this.buttonStyles["rx"]);
			}
			if (this.buttonStyles["ry"]) {
				this.deActivateRect.setAttributeNS(null,"ry",this.buttonStyles["ry"]);
			}
			this.parentGroup.appendChild(this.deActivateRect);
		}
		else {
			alert("could not create or reference 'parentNode' of button with id '"+this.id+"'");			
		}
}

//test if parent group exists
button.prototype.testParent = function() {
    //test if of type object
    var nodeValid = false;
    this.parentGroup = document.createElementNS(svgNS,"g");
    if (typeof(this.parentNode) == "object") {
    	if (this.parentNode.nodeName == "svg" || this.parentNode.nodeName == "g" || this.parentNode.nodeName == "svg:svg" || this.parentNode.nodeName == "svg:g") {
    		this.parentNode.appendChild(this.parentGroup);
    		nodeValid = true;
    	}
    }
    else if (typeof(this.parentNode) == "string") { 
    	//first test if button group exists
    	if (!document.getElementById(this.parentNode)) {
        	this.parentGroup.setAttributeNS(null,"id",this.parentNode);
        	document.documentElement.appendChild(this.parentGroup);
        	nodeValid = true;
   		}
   		else {
       		document.getElementById(this.parentNode).appendChild(this.parentGroup);
       		nodeValid = true;
   		}
   	}
   	return nodeValid;
}

//move button to a new position
button.prototype.moveTo = function(moveX,moveY) {
	this.x = moveX;
	this.y = moveY;
	//case rect
	if (this.buttonType == "rect") {
		this.upperLeftShadow.setAttributeNS(null,"x",this.x - this.shadowOffset);
		this.upperLeftShadow.setAttributeNS(null,"y",this.y - this.shadowOffset);
		this.lowerRightShadow.setAttributeNS(null,"x",this.x + this.shadowOffset);
		this.lowerRightShadow.setAttributeNS(null,"y",this.y + this.shadowOffset);
		this.buttonRect.setAttributeNS(null,"x",this.x);
		this.buttonRect.setAttributeNS(null,"y",this.y);
	}
	else if (this.buttonType == "ellipse") {
		this.upperLeftShadow.setAttributeNS(null,"cx",this.x + this.width * 0.5 - this.shadowOffset);
		this.upperLeftShadow.setAttributeNS(null,"cy",this.y + this.height * 0.5 - this.shadowOffset);
		this.lowerRightShadow.setAttributeNS(null,"cx",this.x + this.width * 0.5 + this.shadowOffset);
		this.lowerRightShadow.setAttributeNS(null,"cy",this.y + this.height * 0.5 + this.shadowOffset);
		this.buttonRect.setAttributeNS(null,"cx",this.x + this.width * 0.5);
		this.buttonRect.setAttributeNS(null,"cy",this.y + this.height * 0.5);
	}
	//reposition button text
	if (this.buttonText != undefined) {
		this.parentGroup.removeChild(this.buttonTextElement);
		this.createButtonTexts();
	}
}

//resize button to a new width and height
button.prototype.resize = function(newWidth,newHeight) {
	this.width = newWidth;
	this.height = newHeight;
	//case rect
	if (this.buttonType == "rect") {
		this.upperLeftShadow.setAttributeNS(null,"width",this.width);
		this.upperLeftShadow.setAttributeNS(null,"height",this.height);
		this.lowerRightShadow.setAttributeNS(null,"width",this.width);
		this.lowerRightShadow.setAttributeNS(null,"height",this.height);
		this.buttonRect.setAttributeNS(null,"width",this.width);
		this.buttonRect.setAttributeNS(null,"height",this.height);
	}
	else if (this.buttonType == "ellipse") {
		this.upperLeftShadow.setAttributeNS(null,"rx",this.width * 0.5);
		this.upperLeftShadow.setAttributeNS(null,"ry",this.height * 0.5);
		this.lowerRightShadow.setAttributeNS(null,"rx",this.width * 0.5);
		this.lowerRightShadow.setAttributeNS(null,"ry",this.height * 0.5);
		this.buttonRect.setAttributeNS(null,"rx",this.width * 0.5);
		this.buttonRect.setAttributeNS(null,"ry",this.height * 0.5);
	}
	if (this.buttonText != undefined) {
		this.parentGroup.removeChild(this.buttonTextElement);
		this.createButtonTexts();
	}
}

//remove all button elements
button.prototype.removeButton = function() {
	this.parentGroup.removeChild(this.upperLeftShadow);
	this.parentGroup.removeChild(this.lowerRightShadow);
	this.parentGroup.removeChild(this.buttonRect);
	if (this.buttonTextElement) {
		this.parentGroup.removeChild(this.buttonTextElement);
	}
	if (this.buttonSymbolInstance) {
		this.parentGroup.removeChild(this.buttonSymbolInstance);
	}
	this.parentGroup.removeChild(this.deActivateRect);
}

//hide button
button.prototype.hideButton = function() {
	this.parentGroup.setAttributeNS(null,"display","none");
}

//show button
button.prototype.showButton = function() {
	this.parentGroup.setAttributeNS(null,"display","inherit");
}

//event handling
button.prototype.handleEvent = function(evt) {
	if (evt.type == "mousedown") {
		this.togglePressed("pressed");
		document.documentElement.addEventListener("mouseup",this,false);
	}
	if (evt.type == "mouseup") {
		this.togglePressed("released");
		document.documentElement.removeEventListener("mouseup",this,false);
	}
	if (evt.type == "click") {
		//for some strange reasons I could not forward the evt object here ;-(, the code below using a literal is a workaround
		//attention: only some of the evt properties are forwarded here, you can add more, if you need them
		var timerEvt = {x:evt.clientX,y:evt.clientY,type:evt.type,detail:evt.detail,timeStamp:evt.timeStamp}
		this.timer.setTimeout("fireFunction",this.timerMs,timerEvt);
	}
}

button.prototype.togglePressed = function(type) {
	if (type == "pressed") {
		for (var attrib in this.shadeDarkStyles) {
			this.upperLeftShadow.setAttributeNS(null,attrib,this.shadeDarkStyles[attrib]);
		}
		for (var attrib in this.shadeLightStyles) {
			this.lowerRightShadow.setAttributeNS(null,attrib,this.shadeLightStyles[attrib]);
		}
	}
	if (type == "released") {
		for (var attrib in this.shadeDarkStyles) {
			this.lowerRightShadow.setAttributeNS(null,attrib,this.shadeDarkStyles[attrib]);
		}
		for (var attrib in this.shadeLightStyles) {
			this.upperLeftShadow.setAttributeNS(null,attrib,this.shadeLightStyles[attrib]);
		}
	}
}

button.prototype.fireFunction = function(evt) {
	if (typeof(this.functionToCall) == "function") {
		if (this.buttonTextElement) {
			this.functionToCall(this.id,evt,this.buttonText);
		}
		if (this.buttonSymbolInstance) {
			this.functionToCall(this.id,evt);
		}
	}
	if (typeof(this.functionToCall) == "object") {
		if (this.buttonTextElement) {
			this.functionToCall.buttonPressed(this.id,evt,this.buttonText);
		}
		if (this.buttonSymbolInstance) {
			this.functionToCall.buttonPressed(this.id,evt);
		}
	}
	if (typeof(this.functionToCall) == undefined) {
		return;
	}
}

button.prototype.getTextValue = function() {
	return this.buttonText;
}

button.prototype.setTextValue = function(value) {
	this.buttonText = value;
	//remove previous buttonTextElement
	if (this.buttonTextElement) {
		this.parentGroup.removeChild(this.buttonTextElement);		
	}
	this.createButtonTexts();
}

//this is a helper function to create the text elements, it is used from two different methods, .createButton() and .setTextValue()
button.prototype.createButtonTexts = function() {
	if (this.buttonText != undefined) {
		//first see if there is a multiline button
		var buttonTexts = String(this.buttonText).split("\n");
		
		//set a start y-offset
		var dy = this.textStyles["font-size"]*1.1;
		
		//if you don't like our method for y-positioning here, you can experiment with the formula here and define your own "initY" 
		var initY = (this.height - dy * buttonTexts.length) / 2 + this.textStyles["font-size"];
		
		//create text element
		this.buttonTextElement = document.createElementNS(svgNS,"text");
		this.buttonTextElement.setAttributeNS(null,"x",(this.x + this.width / 2));
		this.buttonTextElement.setAttributeNS(null,"y",(this.y + initY));			
		for (var attrib in this.textStyles) {
			value = this.textStyles[attrib];
			this.buttonTextElement.setAttributeNS(null,attrib,value);
		}
		this.buttonTextElement.setAttributeNS(null,"pointer-events","none");
		this.buttonTextElement.setAttributeNS(null,"text-anchor","middle");
		this.buttonTextElement.setAttributeNS("http://www.w3.org/XML/1998/namespace","space","preserve");
		for (var j=0;j<buttonTexts.length;j++) {
			var tspan = document.createElementNS(svgNS,"tspan");
			tspan.setAttributeNS(null,"x",(this.x + this.width / 2));
			if (j == 0) {
				tspan.setAttributeNS(null,"dy",0);
			}
			else {
				tspan.setAttributeNS(null,"dy",dy);
			}
			var textNode = document.createTextNode(buttonTexts[j]);
			tspan.appendChild(textNode);
			this.buttonTextElement.appendChild(tspan);
		}
		this.parentGroup.appendChild(this.buttonTextElement);
	}	
}

button.prototype.activate = function(value) {
	this.deActivateRect.setAttributeNS(null,"display","none");
	this.activated = true;
}

button.prototype.deactivate = function(value) {
	this.deActivateRect.setAttributeNS(null,"display","inherit");
	this.parentGroup.appendChild(this.deActivateRect);
	this.activated = false;
}

//switchbutton
//initialize inheritance
switchbutton.prototype = new button();
switchbutton.prototype.constructor = switchbutton;
switchbutton.superclass = button.prototype;

function switchbutton(id,parentNode,functionToCall,buttonType,buttonText,buttonSymbolId,x,y,width,height,textStyles,buttonStyles,shadeLightStyles,shadeDarkStyles,shadowOffset) {
	var nrArguments = 15;
	if (arguments.length > 0) {
		if (arguments.length == nrArguments) {
			this.init(id,parentNode,functionToCall,buttonType,buttonText,buttonSymbolId,x,y,width,height,textStyles,buttonStyles,shadeLightStyles,shadeDarkStyles,shadowOffset);
		}
		else {
			alert("Error in Switchbutton ("+id+"): wrong nr of arguments! You have to pass over "+nrArguments+" parameters.");		
		}		
	}
}

switchbutton.prototype.init = function(id,parentNode,functionToCall,buttonType,buttonText,buttonSymbolId,x,y,width,height,textStyles,buttonStyles,shadeLightStyles,shadeDarkStyles,shadowOffset) {
	switchbutton.superclass.init.call(this,id,parentNode,functionToCall,buttonType,buttonText,buttonSymbolId,x,y,width,height,textStyles,buttonStyles,shadeLightStyles,shadeDarkStyles,shadowOffset);
	this.on = false;
}

//overwriting handleEventcode
switchbutton.prototype.handleEvent = function(evt) {
	//for some strange reasons I could not forward the evt object here ;-(, the code below using a literal is a workaround
	//attention: only some of the evt properties are forwarded here, you can add more, if you need them
	var timerEvt = {x:evt.clientX,y:evt.clientY,type:evt.type,detail:evt.detail,timeStamp:evt.timeStamp}
	if (evt.type == "click") {
		if (this.on) {
			this.on = false;
			this.togglePressed("released");
			this.timer.setTimeout("fireFunction",this.timerMs,timerEvt);
		}
		else {
			this.on = true;
			this.togglePressed("pressed");
			this.timer.setTimeout("fireFunction",this.timerMs,timerEvt);
		}
	}
}

switchbutton.prototype.getSwitchValue = function() {
	return this.on;
}

switchbutton.prototype.setSwitchValue = function(onOrOff,firefunction) {
	this.on = onOrOff;
	//artificial timer event - don't use the values!
	var timerEvt = {x:0,y:0,type:"click",detail:1,timeStamp:0}
	if (this.on) {
		this.togglePressed("pressed");
		if (firefunction) {
			this.timer.setTimeout("fireFunction",this.timerMs,timerEvt);
		}
	}
	else {
		this.togglePressed("released");
		if (firefunction) {
			this.timer.setTimeout("fireFunction",this.timerMs,timerEvt);
		}
	}
}

//overwriting fireFunction code
switchbutton.prototype.fireFunction = function(evt) {
	if (typeof(this.functionToCall) == "function") {
		if (this.buttonTextElement) {
			this.functionToCall(this.id,evt,this.on,this.buttonText);
		}
		if (this.buttonSymbolInstance) {
			this.functionToCall(this.id,evt,this.on);
		}
	}
	if (typeof(this.functionToCall) == "object") {
		if (this.buttonTextElement) {
			this.functionToCall.buttonPressed(this.id,evt,this.on,this.buttonText);
		}
		if (this.buttonSymbolInstance) {
			this.functionToCall.buttonPressed(this.id,evt,this.on);
		}
	}
	if (typeof(this.functionToCall) == undefined) {
		return;
	}
}
